/**
Company: Sarkar software technologys
WebSite: http://www.sarkar.cn
Author: yeganaaa
Date : 6/15/18
Time: 4:56 PM
 */

package Cn.Sarkar.EntityDream.CoreEngine.RDBMS

import Cn.Sarkar.EntityDream.CoreEngine.RDBMS.Core.Extensions.*
import Cn.Sarkar.EntityDream.CoreEngine.RDBMS.Core.IDBTable
import Cn.Sarkar.EntityDream.CoreEngine.RDBMS.Core.Index
import Cn.Sarkar.EntityDream.CoreEngine.RDBMS.Core.PipeLine.CorePipeLine
import Cn.Sarkar.EntityDream.CoreEngine.RDBMS.Core.PipeLine.IPipeLineSubject
import Cn.Sarkar.EntityDream.CoreEngine.RDBMS.Core.PipeLine.Subjects.TranslationSubject
import Cn.Sarkar.EntityDream.CoreEngine.RDBMS.Core.QueryBuilderExtensions.SelectQueryExpression.*
import Cn.Sarkar.EntityDream.CoreEngine.RDBMS.MySql.MySqlDataContext
import Cn.Sarkar.EntityDream.CoreEngine.RDBMS.MySql.createDataBaseIfNotExists
import Cn.Sarkar.EntityDream.CoreEngine.RDBMS.TestAll.Initialize.createDataBasesAndTables
import Cn.Sarkar.EntityDream.Pipeline.Core.Info.FeatureInfo
import Cn.Sarkar.EntityDream.Pipeline.Core.PipeLineContext
import Cn.Sarkar.EntityDream.Pipeline.Core.PipeLineFeature
import Cn.Sarkar.EntityDream.Pipeline.Core.PipeLineFeatureMetaData
import Cn.Sarkar.EntityDream.Pipeline.Extension.installFeature
import org.junit.Assert
import org.junit.Test
import java.sql.DriverManager

internal class TestAll {
    object Interfaces {
        interface ICompany {
            var ID: Int
            var Parent: ICompany?
            var WebSite: String
        }

        interface IPerson {
            var ID: Int
            var Name: String
            var SignInName: String
            var Password: String
            var Male: Boolean
            var IDCardNumber: String
            var Company: ICompany
        }
    }

    object Tables {
        object Companies : DBTable("Company") {
            override var Comment: String = "شىركەت"
            val ID = idColumn("ID") comment "تەرتىپ نومۇرى"
            val ParentID = intColumn("ParentID") reference Companies.ID ignoreCreateForeignKey true comment "دادىسى"
            val WebSite = stringColumn("WebSite") isN false size 50 comment "تور بەت ئادېرسى"

            init {
                unique(WebSite)
            }
        }

        object Admins : DBTable("CompanyAdmin") {
            override var Comment: String = "باشقۇرغۇچىلار"
            val PersonID = intColumn("PersonID") reference Persons.ID comment "ئادەم"
            val CompanyID = intColumn("CompanyID") reference Companies.ID comment "شىركەت"
            val CreationDateTime = dateTimeColumn("CreationDateTime")
            val Addtional = longColumn("Addtional")

            init {
                unique(PersonID, CompanyID)
                index(CreationDateTime)
            }
        }

        object Persons : DBTable("Person") {
            override var Comment: String = "ئادەملەر"
            val ID = idColumn("ID") comment "تەرتىپ نومۇرى"
            val Name = stringColumn("Name") comment "ئىسمى"
            val SignInName = stringColumn("SignInName") indexLength 16 size 16 comment "كىرىش ئىسمى"
            val Password = stringColumn("Password") indexLength 16 size 16 comment "پارول"
            val Male = booleanColumn("Male") comment "ئوغۇل بالىمۇ"
            val IDCardNumber = charColumn("IDCardNumber") isN false size 18 comment "كىملىك نومۇرى"
            val CompanyID = intColumn("CompanyID") reference Companies.ID comment "شىركىتى"

            init {
                index(Name, Password) unique true
                index(IDCardNumber) unique true
            }
        }
    }

    object Entities {
        class Company(DataContext: IDataContext) : DBEntity(DataContext, Tables.Companies), Interfaces.ICompany {
            override var ID: Int by Tables.Companies.ID
            override var WebSite: String by Tables.Companies.WebSite
            var ParentID by Tables.Companies.ParentID
            override var Parent: Interfaces.ICompany? by hasOneNullable(Tables.Companies.ParentID) { Company(it) }
            val persons by hasMany(Tables.Persons.CompanyID) { Person(it) }
            val childs by hasMany(Tables.Companies.ParentID) { Company(it) }
        }

        class Person(DataContext: IDataContext) : DBEntity(DataContext, Tables.Persons), Interfaces.IPerson {
            override var ID: Int by Tables.Persons.ID
            override var Name: String by Tables.Persons.Name
            override var SignInName: String by Tables.Persons.SignInName
            override var Password: String by Tables.Persons.Password
            override var Male: Boolean by Tables.Persons.Male
            override var IDCardNumber: String by Tables.Persons.IDCardNumber
            var CompanyID by Tables.Persons.CompanyID
            override var Company: Interfaces.ICompany by hasOne(Tables.Persons.CompanyID) { Company(it) }
        }

        class Admin(DataContext: IDataContext) : DBEntity(DataContext, Tables.Admins) {
            var personID by Tables.Admins.PersonID
            var person by hasOne(Tables.Admins.PersonID) { Person(it) }
            var companyID by Tables.Admins.CompanyID
            var company by hasOne(Tables.Admins.CompanyID) { Company(it) }
            var addtional by Tables.Admins.Addtional
            var creationTime by Tables.Admins.CreationDateTime
        }
    }

    class DataSource : MySqlDataContext(connection) {
        override val tables: Array<IDBTable> = arrayOf(Tables.Companies, Tables.Admins, Tables.Persons)

        val companies = dbCollection(Tables.Companies) { Entities.Company(it) }
        val persons = dbCollection(Tables.Persons) { Entities.Person(it) }
        val admins = dbCollection(Tables.Admins) { Entities.Admin(it) }

        init {

            val logger = object : PipeLineFeature<IPipeLineSubject, IDataContext>() {

                override val getMetaData: PipeLineFeatureMetaData by lazy { PipeLineFeatureMetaData(CorePipeLine.after, "Logger") }
                override val info: FeatureInfo by lazy { FeatureInfo("Logger", "Demo", "Sarkar Software Technologys", "yeganaaa", 1, "v0.1") }

//        var index = 0

                override fun PipeLineContext<IPipeLineSubject, IDataContext>.onExecute(subject: IPipeLineSubject) {
//                println((++index).toString() + "---" + subject.Name + "***********" + subject::class.java.simpleName)

                    if (subject is TranslationSubject) {

                        println("Generated SQL ->> " + subject.translationResult!!.fullSqlQuery)
                    }
                }
            }

            pipeLine.installFeature(logger)
        }

        companion object {
            //            val connection = DriverManager.getConnection("jdbc:h2:mem:;MODE=MYSQL")
//            val connection by lazy {DriverManager.getConnection("jdbc:h2:mem:;MODE=MYSQL")}
            val connection by lazy { DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/Hello", "root", "WeAreDevelopers") }
            val superConnection by lazy { DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306", "root", "WeAreDevelopers") }
        }
    }

    open class EmbeddedDB {
        val db by lazy { DataSource() }
    }

    object Initialize : EmbeddedDB() {
        @Test
        fun createDataBasesAndTables() {
            createDataBaseIfNotExists(DataSource.superConnection, "Hello")
            if (!db.isExistsTableOf(Tables.Companies)) {
                db.createNewTables()
            }
        }
    }

    class DataOperations : EmbeddedDB() {
        val sarkar_website = "http://www.sarkar.cn"
        val ug_project_website = "http://www.ug-project.cc"
        val designers_website = "http://www.designer.sarkar.cn"

        @Test
        fun insertData() {

            var sarkar = db.companies.singleOrNull { Tables.Companies.WebSite equals sarkar_website }
            if (sarkar == null) {
                sarkar = Entities.Company(db).apply {
                    this.WebSite = sarkar_website
                }
                db.companies.add(sarkar)
                db.saveChanges()
            }
            /**
             * Redirect ID
             */
            assert(sarkar.ID > -1)


            var ug_project = db.companies.singleOrNull { Tables.Companies.WebSite equals ug_project_website }
            if (ug_project == null) {
                ug_project = Entities.Company(db).apply {
                    this.WebSite = ug_project_website
                    this.Parent = sarkar
                }
                db.companies.add(ug_project)
            }

            var designers = db.companies.singleOrNull { Tables.Companies.WebSite equals designers_website }
            if (designers == null) {
                designers = Entities.Company(db).apply {
                    WebSite = designers_website
                    ParentID = sarkar.ID
                }
                db.companies.add(designers)
            }

            //executeBatch
            db.saveChanges()

            val selected_ug_project = db.companies.single { Tables.Companies.WebSite equals ug_project_website }

            println(sarkar.Parent?.WebSite)
            println(selected_ug_project.Parent?.WebSite)

            val webSites = db.companies.where { Tables.Companies.WebSite notEquals "" }.map { it.WebSite }
            webSites.forEach {
                println(it)
            }


            var yeganaaa = db.persons.singleOrNull { Tables.Persons.SignInName equals "yeganaaa" and (Tables.Persons.Password equals "653125") }
            if (yeganaaa == null) {
                yeganaaa = Entities.Person(db).apply {
                    this.Company = ug_project
                    this.IDCardNumber = "653125199403015256"
                    this.Male = true
                    this.Name = "مۇختەرجان"
                    this.SignInName = "yeganaaa"
                    this.Password = "653125"
                }
                db.persons.add(yeganaaa)
            }

            var abdukader = db.persons.singleOrNull { Tables.Persons.SignInName equals "yeganaaa" and (Tables.Persons.Password equals "653125") }
            if (abdukader == null) {
                abdukader = Entities.Person(db).apply {
                    this.Company = ug_project
                    this.IDCardNumber = "653124199403015256"
                    this.Male = true
                    this.Name = "ئابدۇقادىر"
                    this.SignInName = "abdukader"
                    this.Password = "000000"
                }
                db.persons.add(abdukader)
            }

            db.saveChanges()
        }

        @Test
        fun selectParent() {
            val selected_sarkar = db.companies.single { Tables.Companies.WebSite equals sarkar_website }
            selected_sarkar.childs.forEach {
                println(it.WebSite)
            }
        }

        @Test
        fun selectMany()
        {
            val company = db.companies.single { Tables.Companies.WebSite equals ug_project_website }.persons.forEach {
                println(it.Name)
            }
        }
    }

    @Test
    fun testMain() {
        createDataBasesAndTables()
        DataOperations().insertData()
    }
}
